#include <stdlib.h>
#include <stdio.h>

#include "TDA_Lista.h"

/* =============================================================================
 * Declaracion Atributos privados
 * =============================================================================
 */
#define LISTA_FIN NULL

#define TRUE 1
#define FALSE 0

struct Nodo {
  tDato dato;
  struct Nodo* sig;
};

typedef struct Nodo* tNodo;

/*==============================================================================
 * Declaracion de metodos privados
 *==============================================================================
 */

static tNodo crearNodo (tDato pDato);

static void listaDestruirNodo (tNodo pNodo);

static tNodo nodoGetDato (tNodo pNodo);

static tNodo listaRemoverNodo (tLista pLista, tNodo pNodo);

//==============================================================================

struct IteradorLista {
  tLista lista;
  tNodo cursor;
  int enInicio;
};

//==============================================================================

struct Lista {
  tNodo raiz;
  tFcnDestructorDato fcnDestructorDato;
};

typedef struct Lista* _tLista;

/*==============================================================================
 * Definicion de metodos publicos
 *==============================================================================
 */

void listaCrear (tLista* pLista, tFcnDestructorDato pFcnDestructorDato){

  if (pLista && pFcnDestructorDato)
  {
    tLista lista = malloc (sizeof (struct Lista));
    
    if (lista)
    {
      lista->raiz = LISTA_FIN;
      lista->fcnDestructorDato = pFcnDestructorDato; 
    }  
    *pLista = lista;
  }
}

//------------------------------------------------------------------------------

void listaDestruir (tLista pLista){

    if (pLista)
    {
        while (!listaVacia(pLista))
        {
            tNodo temp = listaRemoverNodo (pLista, pLista->raiz);
            if (NULL != temp) 
            {
                pLista->fcnDestructorDato(temp->dato);
                listaDestruirNodo (temp);
            }
        }
        free ((_tLista) pLista);
    }
}

//------------------------------------------------------------------------------

int listaAgregarFinal (tLista pLista, tDato pDato){
  
  int agregado = FALSE;
    
  if (pLista && pDato)
  {
    tNodo nodo =  crearNodo (pDato);
    if (nodo)
    {
      agregado = TRUE;      
      if (listaVacia (pLista))
        pLista->raiz = nodo;
      else
      {
        //Busco el ultimo nodo de la lista.
        tNodo cursor = pLista->raiz;
        
        while (LISTA_FIN != cursor->sig)
            cursor = cursor->sig;
        
        cursor->sig = nodo;
      }
    }
  }  
  return agregado;
}

//------------------------------------------------------------------------------

tDato listaRemover (tLista pLista, tDato pDato) {
  
    if (!listaVacia (pLista))
  {
    // Busco el nodo que contiene al dato.
    tNodo cursor = pLista->raiz;
    while (LISTA_FIN != cursor && cursor->dato != pDato)
      cursor = cursor->sig;
    
    if (LISTA_FIN != cursor)
    {
      // Encontre al nodo que contiene al dato.
      cursor = listaRemoverNodo(pLista, cursor);
      if (cursor)
      {
        listaDestruirNodo(cursor);
      }
    }
    else
      // El dato no esta en la lista.
      pDato = NULL;
  }
  else
    // El dato no esta en la lista.
    pDato = NULL;
  
  return pDato;
}

//------------------------------------------------------------------------------

tDato listaObtenerPrimero(tLista pLista){
    
    tDato primero = NULL;
    
    if (pLista)
        primero = nodoGetDato (((struct Lista*) pLista)->raiz);
    
    return primero;
}

//------------------------------------------------------------------------------

tDato listaObtenerSiguiente(tLista pLista, tDato pDato) {
    
    tDato dato = NULL;
    
    if (pLista && pDato)
    {
        tNodo nodo = ((_tLista)pLista)->raiz;
        
        while (NULL != nodo && dato == NULL)
        {
            if (nodoGetDato(nodo) == pDato)
                dato = pDato;
            nodo = nodo->sig;
        }
        
        if (NULL != dato)    
            dato = nodoGetDato(nodo);
    }
    return dato;
}

//------------------------------------------------------------------------------

int listaVacia (tLista pLista){

  int vacia = TRUE;

  if (pLista)
    vacia = pLista->raiz == LISTA_FIN;

  return vacia;
}

//------------------------------------------------------------------------------

int listaLongitud(tLista pLista){
    
    int longitud = 0;
    
    if (pLista)
    {
        tIteradorLista iterador = listaObtenerIterador(pLista);
        while (listaIteradorMoverSiguiente(iterador))
            longitud ++;
        
        listaLiberarIterador(iterador);
    }
    return longitud;
}

//------------------------------------------------------------------------------

tIteradorLista listaObtenerIterador (tLista pLista){

  tIteradorLista iterador = NULL;

  if (pLista)
  {
    iterador = malloc (sizeof (struct IteradorLista));
    if (iterador)
    {
      iterador->lista = pLista;
      iterador->cursor = NULL;
      iterador->enInicio = TRUE;
    }
  } 
  return iterador;
}

//------------------------------------------------------------------------------

void listaLiberarIterador (tIteradorLista pIterador){

  if (pIterador)
  {
    pIterador->lista = NULL;
    pIterador->cursor = NULL;
    free (pIterador);
  }
}

//------------------------------------------------------------------------------

tBool listaIteradorMoverPrimero (tIteradorLista pIterador){
    
    tBool val = FALSE;
    
    if (pIterador)
    {
        pIterador->enInicio = TRUE;
        pIterador->cursor = NULL;
        
        val = TRUE;
    }
    
    return val;
}

//------------------------------------------------------------------------------

tBool listaIteradorMoverSiguiente (tIteradorLista pIterador){
  
  tBool exito = FALSE;
  
  if (pIterador)  
    if (!listaVacia (pIterador->lista))
    {
      if (TRUE == pIterador->enInicio)
      {
        pIterador->cursor = pIterador->lista->raiz;
        pIterador->enInicio = FALSE;
      }
      else if (LISTA_FIN != pIterador->cursor)
        pIterador->cursor = pIterador->cursor->sig;
        
      exito = pIterador->cursor != LISTA_FIN;

    }
  return exito;
}

//------------------------------------------------------------------------------

tDato listaIteradorActual(tIteradorLista pIterador){
  
  tDato val = NULL;
  
  if (pIterador)
  {
    if (LISTA_FIN != pIterador->cursor)
      val = pIterador->cursor->dato;    
  }  
  return val;
}

/* =============================================================================
 * Definicion Metodos privados
 * =============================================================================
 */

static tNodo listaRemoverNodo (tLista pLista, tNodo pNodo)
{

  if (pLista && pNodo)
  {
    tNodo cursor = pLista->raiz
        , previo = NULL;  
   
    while (cursor != pNodo && LISTA_FIN != cursor)
    {
      if (cursor != pNodo)
      {
        previo = cursor;
        cursor = cursor->sig;
      }
    }
    if (cursor == pNodo)
    {
      if (NULL == previo)
      {
        pLista->raiz = pNodo->sig;
      }
      else
      {
        previo->sig = pNodo->sig;
      }
    }
    else
      pNodo = NULL;
  }
  else
    pNodo = NULL;
    
  return pNodo;
}

//------------------------------------------------------------------------------

void listaDestruirNodo (tNodo pNodo)
{
  if (pNodo)
    free (pNodo);
}

//------------------------------------------------------------------------------

static tNodo crearNodo (tDato pDato)
{
  tNodo nodo = NULL;
  
  if (pDato)
  {
    nodo = malloc (sizeof (struct Nodo));
    if (nodo)
    {
      nodo->dato = pDato;
      nodo->sig = LISTA_FIN;
    }
  }
  return nodo;
}

//------------------------------------------------------------------------------

static tNodo nodoGetDato (tNodo pNodo){
    
    tDato dato = NULL;
    
    if (pNodo)
        dato = pNodo->dato;
    
    return dato;
}
